如果我调用Factory.build以使我的控制器测试快速,如何使Factory Girl永远不要打数据库?

如何解决如果我调用Factory.build以使我的控制器测试快速,如何使Factory Girl永远不要打数据库?

| 我正在寻求使Rails测试更快。我只有520个测试,但是在bash中运行需要62秒,在Rubymine中运行需要82秒。 作为一个典型的控制器测试的示例,我使用以下代码以@user身份登录并在我的RSpec控制器测试的CommentsController中创建基本的@comment:
before(:each) do
  @user = Factory.create(:user)
  sign_in @user

  @comment = Factory.create(:comment)
end
您可能会意识到...这很慢。它建立一个“ 1”,但也建立该用户的关联。
@comment
也一样。 所以我以为打
Factory.build(:user)
就可以解决...但是我遇到了奇怪的错误。例如,
current_user
返回
nil
。 所以...我决定使用
Factory.build()
,并在父控制器中存入所有之前的过滤器。但是,我的rspec日志仍然说,当我随后检查RSPec日志时,数据库中会插入大量插入内容(我们仅通过3个测试就讨论了数百行代码!)
  before(:each) do
    @user = Factory.build(:user)
    #sign_in @user

    controller.stub(:authenticate_user!) #before_filter
    controller.stub(:add_secure_model_data) #before_filter
    controller.stub(:current_user).and_return(@user)

    @comment = Factory.build(:comment)
  end
可悲的事实是,上面的“ 8”区块对测试性能具有零影响。正如我发现的那样,在子关联上调用
Factory.build()
仍会在内部调用
Factory.create()
。 这是一个
before(:each)
块,可有效去除RSpec日志中产生的垃圾。它使我的测试性能提高了35-40%
  before(:each) do
    @user = Factory.build(:user,:role => Factory.build(:role))
    #sign_in @user

    controller.stub(:authenticate_user!)
    controller.stub(:add_secure_model_data)
    controller.stub(:current_user).and_return(@user)

    # both of these are still super slow. WTF?!
    @site_update = Factory.build(:site_update,:id => 5,:author => Factory.build(:user,:role => Factory.build(:role)))

    @comment = Factory.build(:comment,:role => Factory.build(:role)),:commentable => @site_update)
  end
这使测试运行速度更快,但也很丑陋。我们不能为每项测试认真写这个……对吗?真是疯了。我没有这样做。 我还想指出的是,即使这些
Factory.build()
行未触及数据库,它们中的任何一条仍需要约0.15秒! 仅运行3个测试仍会导致factory_girl PER测试占用约0.3到0.35秒的时间!我认为这是完全不能接受的。如果删除“ 6”行,则测试将在0.00001秒内运行。 我认为评审团在:factory_girl是一个非常慢的库。是唯一不使用它的解决方案吗? 这是我的
factories.rb
Factory.define :role do |f|
  f.name \"Admin\"
end

Factory.define :user do |f|
  f.first_name \"Banoo\"
  f.last_name \"Smith\"
  f.sequence(:email) { |n| \"Banoo.Smith#{n}@gmail.com\" }
  f.password \"secretpassword\"
  f.association :role
end

Factory.define :admin do |f|
  f.first_name \"Banoo\"
  f.last_name \"Smith\"
  f.sequence(:email) { |n| \"admin#{n}@gmail.com\" }
  f.password \"secretpassword\"
  f.association :role
end

Factory.define :course_provider do |f|
  f.first_name \"Josh\"
  f.last_name \"Bolson\"
  f.sequence(:email) { |n| \"josh.bolson#{n}@gmail.com\" }
  f.password \"secretpassword\"
  f.association :role
end

Factory.define :director do |f|
  f.first_name \"Director\"
  f.last_name \"Dude\"
  f.sequence(:email) { |n| \"director#{n}@gmail.com\" }
  f.password \"secretpassword\"
  f.association :role
end

Factory.define :instructor do |f|
  f.first_name \"Instructor\"
  f.last_name \"Dude\"
  f.sequence(:email) { |n| \"instructor#{n}@gmail.com\" }
  f.password \"secretpassword\"
  f.association :role
end

Factory.define :trainee do |f|
  f.first_name \"Trainee\"
  f.last_name \"Dude\"
  f.sequence(:email) { |n| \"trainee#{n}@gmail.com\" }
  f.password \"secretpassword\"
  f.association :role
end

Factory.define :private_message do |f|
  f.subject \"Subject\"
  f.content \"content\"
  f.is_deleted_by_sender false
  f.association :sender,:factory => :user
end

Factory.define :recipient do |f|
  f.is_read false
  f.is_deleted false
  f.association :receiver,:factory => :user
  f.association :private_message
end

Factory.define :course_template do |f|
  f.name \"name\"
  f.description \"description\"
  f.association :course_provider
end

Factory.define :site_update do |f|
  f.subject \"Subject\"
  f.intro \"intro\"
  f.content \"content\"
  f.association :author,:factory => :user
end

Factory.define :comment do |f|
  f.content \"content\"
  f.association :author,:factory => :user
  f.association :commentable,:factory => :site_update
end

Factory.define :country do |f|
  f.name \"Liberty\"
end

Factory.define :province do |f|
  f.name \"Freedom\"
  f.association :country
end

Factory.define :payment_plan do |f|
  f.name \"name\"
  f.monthly_amount 79
  f.audience \"Enterprises\"
  f.active_courses \"500-2000\"
end

Factory.define :company do |f|
  f.name \"name\"
  f.phone_number \"455-323-2132\"
  f.address \"address\"
  f.postal_code \"N7G-5F4\"
  f.association :province
  f.association :payment_plan
end

Factory.define :company_user do |f|
  f.first_name \"Dan\"
  f.last_name \"Grayson\"
  f.sequence(:email) { |n| \"dan.grayson#{n}@gmail.com\" }
  f.password \"secretpassword\"
  f.association :role
  f.association :company
end

Factory.define :course do |f|
  f.notes \"notes\"
  f.difficulty 100
  f.association :course_template
  f.association :instructor,:factory => :company_user
end

Factory.define :study_group do |f|
  f.name \"name\"
end

Factory.define :help_category do |f|
  f.name \"name\"
end

Factory.define :help_document do |f|
  f.question \"question\"
  f.content \"content\"
  f.association :category,:factory => :help_category
end

Factory.define :tag do |f|
  f.name \"name\"
end

Factory.define :partial_mapping do |f|
  f.from_suffix \"ing\"
  f.to_suffix \"ing\"
end

Factory.define :newsletter do |f|
  f.subject \"subject\"
  f.content \"content\"
end

Factory.define :press_contact do |f|
  f.full_name \"Banoo Smith\"
  f.email \'Banoo.Smith@gmail.com\'
  f.phone_number \"455-323-2132\"
  f.address \"address\"
  f.postal_code \"N9B-3W5\"
  f.association :province
end

Factory.define :press_release do |f|
  f.headline \"Headline\"
  f.origin \"origin\"
  f.intro \"intro\"
  f.body \"body\"
  f.association :contact,:factory => :press_contact
end

Factory.define :theme do |f|

end
和有趣的基准。呼叫
Factory.create(:user)
平均需要0.1至.14秒:
$ rails runner \'Benchmark.bm {|x| x.report { 100.times { Factory.create(:user) } } }\' 
      user     system      total        real
  9.940000   0.080000  10.020000 ( 14.872736)
甚至连3英镑也要花掉永远的时间……而打开20英镑时,这是永远的!
$ rails runner \'Benchmark.bm {|x| x.report { 100.times { Factory.build(:user) } } }\'
      user     system      total        real
  9.350000   0.030000   9.380000 ( 11.798339)
显然,这表明factory_girl出了点问题。解决的办法是摆脱它或确保它使用
Factory.build
。那就是答案。 因为我已经基本解决了自己的问题,所以我想知道为什么Factory_girl如此受欢迎,为什么它是“常识”?可以客观地得出结论,使用Factory Girl可以获得任何好处-而且有很多好处-这不值得付出性能成本。我确信可以开发出性能更高的更好的工厂级宝石...但是很不幸,很遗憾,factory_girl不是。 我下面的解决方案使用基本的对象实例化和存根,并且测试继续通过。我认为,如果要避免固定装置并在运行测试时获得高性能,那么在每次测试中使用基本的Ruby,存根并手动填充对象值是“正确的”事情。     

解决方法

好吧,我想我会回答我自己的问题。我认为这是正确的答案,也许其他人可以从中学习,因为我不得不花几个小时来学习它。 这是我如何将速度提高2000%(或20倍)的方法:
before(:each) do
  @user = User.new
  controller.stub(:authenticate_user!)
  controller.stub(:current_user).and_return(@user)
  controller.stub(:add_secure_model_data)

  @site_update = SiteUpdate.new
  @comment = Comment.new
end
解决的方法是根本不使用任何类型的工厂进行控制器测试(也许还使用其他类型的测试)。我建议仅在无法忍受的情况下才使用Factory \'s。 现在,所有3个测试都在0.07秒内运行!在1.4秒内运行所有3个测试。 Factory_girl只是一个非常慢的库。我不知道它在做什么,但是没有正确配置它。 是的,我知道它所做的不仅仅是简单的
MyClass.new
语句...但是,即使对于像Ruby这样的慢速脚本语言,其性能也比基本类实例化慢了多个数量级。它需要进行一些大的优化,以使ѭ25与ѭ24更加一致 我建议Factory_girl的实现者尝试并获取它,以使其开销不会比基本的
MyClass.new
调用慢(不排除数据库开销...这是无法避免的)。它应该提供一种构建对象的好方法,并且您不必为此付出20倍的性能损失。这不是一个可以接受的折衷方案。 这真的太糟糕了,因为当在控制器规格中打开
render_views
时,
Factory.build
在控制器中会很好。应该有很大的动力去纠正这个问题。 同时,只需使用基本的Ruby / Rails类。我想您会惊讶他们的实际速度如此之快。     ,我遇到了与@FireEmblem相同的问题,最终将问题缩小到30英镑。
FactoryGirl.stub
并没有使事情变得更好。 我最终意识到这是因为我的一个模型具有验证逻辑,该逻辑在存在特定字段时发出HTTP请求。工厂在该字段中设置了一个值,因此在外部,看起来FactoryFactory正在减慢我的测试速度。实际上,它是(但仅因为)它触发了HTTP请求。从我的一家工厂中删除一条线路消除了HTTP请求,从而使性能提高了60倍。     

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?