Shoulda + FactoryGirl:我可以更快地进行测试吗?

时间:2009-11-03 22:57:52

标签: ruby-on-rails testing shoulda factory-bot

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

我正在尝试测试的模型(StudentExam)与其他模型有关联。在创建StudentExam之前,这些关联对象必须存在。因此,它们是在setup

中创建的

但是,我们的一个模型(School)需要很长时间才能创建。因为setup在每个should语句之前被调用,所以整个测试用例需要很长时间才能执行 - 它会创建一个新的@school@student@topic和{ {1}}为每个执行的语句执行。

我正在寻找一种方法来创建这些对象一次,只有一次。对@exam方法有startup之类的东西可以让我创建记录,这些记录会在测试用例的其余部分中持续存在吗?

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

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

before_all

4 个答案:

答案 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_contextgithub link)的插件,它将should语句组合到一个上下文中,从而加速了测试。

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

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