5

我正在寻找一种方法来加快我的Shoulda + FactoryGirl测试。

我要测试的模型 ( StudentExam) 与其他模型有关联。这些关联对象必须存在,然后我才能创建StudentExam. 因此,它们是在setup.

但是,我们的模型之一 ( School) 需要大量时间来创建。因为setup在每条语句之前都被调用should,所以整个测试用例需要 eons 来执行——它为每个执行的 should 语句创建一个新@school@student,@topic和。@exam

我正在寻找一种方法来创建这些对象一次且仅一次。是否有类似startupforbefore_all方法的东西可以让我创建将在整个测试用例的其余部分持续存在的记录?

基本上我正在寻找与 RSpec 完全相同的东西before(:all)。我不关心依赖问题,因为这些测试永远不会修改那些昂贵的对象。

这是一个示例测试用例。为长代码道歉(我还创建了一个要点):

# 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
4

4 回答 4

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
于 2009-11-03T23:19:00.750 回答
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

基本上,如果您因为构建对象的成本太高而需要加速单元测试,那么您并不是真正的单元测试。上面所有的测试用例都感觉应该在单元测试级别,所以存根存根存根!

于 2009-11-04T02:39:56.500 回答
0

http://m.onkey.org/2009/9/20/make-your-should-tests-faster-with-fast_context是一篇关于如何使用名为快速上下文。让我知道它是否不是您需要的。

于 2009-11-03T23:13:40.147 回答
0

有一个名为fast_contextgithub 链接)的插件将 should 语句组合到一个上下文中,从而加快测试速度。

我用来加速测试的另一件事是预先填充夹具数据。FactoryGirl 很慢,因为它会在每次设置块运行时创建这些记录。

我编写了一个名为Fixie的插件,它使用 ActiveRecord 来预填充测试数据库,因此测试所需的记录已经创建。如果您也想在运行时创建新记录,您可以将 Fixie 与 FactoryGirl 一起使用。

于 2009-11-03T23:17:03.097 回答