Rails 3.0.7 - >你如何让你的测试运行得更快?

时间:2011-05-22 10:27:32

标签: ruby-on-rails ruby performance testing

我正在运行mysql,database_cleaner,Rspec等。到目前为止,我有大约518次测试,它们需要88秒才能运行。这对我来说是不可接受的,因为我的应用程序开发刚刚开始。

因此,在进一步研究之前,我想尝试找到减少运行这些测试所需时间的方法 - 希望无需实际更改测试。

在大多数情况下,我正在尝试使用存根。但是,当我测试模型和查询时,我确实使用了数据库。

我认为database_cleaner正在减慢它们的速度,但我不知道如何在没有它的情况下测试查询和内容。

将sqlite3与“:memory:”选项一起使用似乎只能减少约10秒钟(令人失望的结果......)

如何才能真正加快测试速度?

6 个答案:

答案 0 :(得分:17)

您可以使用各种策略来加快测试时间。如果你刚开始,并且你看到88秒的运行时间,我会想到很多这些适用于你:

  • 使用spork - Spork将执行所有引导和环境需要一次,将其保留在内存中,并仅重新加载测试。这对于快速运行测试非常有帮助。
  • 明智地测试你的测试方法 - 就我个人而言,我的工作流程是开发测试,运行完整的测试套件以查看失败的内容,然后只运行新的/失败的测试,直到我能够获得这些测试绿色。最后,当我完成所有工作后,我最后一次运行套件,看看我是否退回了其他东西。
  • 清理您的Gemfile - 大多数Rails启动时间花费在需求上,如果您有宝石,则不再使用,您需要添加加载时间,无益。取出你不使用的任何东西,并考虑将只在一个或两个位置使用的东西放在一个命名组中,这样你就可以在执行期间手动要求它们(小心这一点 - 你正在交换初始加载速度以满足请求性能,对开发来说很棒,但对于制作很糟糕)
  • 要知道什么是存根和模拟 - 例如,如果你真的在进行单元测试,你应该避免触及数据库,或者至少是你的控制器测试。想想班上的责任究竟是什么。控制器不负责保存记录,它负责告诉模型保存记录。即使模型也不负责保存数据库中的内容,他们负责告诉ActiveRecord。
  • 如果你把事情搞得一团糟,可以考虑不包括Rails 。您需要在测试中删除几乎所有ActiveRecord功能,但如果您可以这样做,您将看到测试时间大量减少(可能超过一个数量级)

答案 1 :(得分:5)

Ryan Brunner提出了许多很好的建议。他说的一切都是正确的,但不适用于我。

我没有提到工厂女孩,因为我没想提到它(不要问)。事实证明这是非常相关的细节,因为它负责测试运行如此缓慢。

通过简单地从我的控制器测试(我使用Factory.build)中删除Factory girl,我已经设法将它们从50秒缩短到类似5的东西。

原因是Factory.build调用Factory.create进行关联,导致数据库命中...所以如果你有很多关联,那么创建一个新的模型对象需要一段时间。但更重要的是,在我的案例中,这仅占开销的30-35%。 Factory_girl实际上花费了65-70%的时间来做非数据库的事情。我不知道为什么,但在强制每次调用Factory.build之后,构建我的对象仍需要很长时间。使用基本MyClass.new最终会更快很多

我的整个测试套件现在需要不到30秒而不是90秒。通过进行这些改变,这通常会增加300%的速度......但是当谈到控制器测试时,我的速度提高了2000% - 而且我已经踩到了!所有这些性能开销都归因于Factory.build!这就是大部分收益来自的地方。

当然,我回到了我的模型中,无论身在何处,我都会使用Factory.buildMyClass.new

我也尽可能在:default_strategy => :build添加了factories.rb,以防止Factory Girl进入数据库。如果你问我,这应该是默认值,因为这次更改只有1次测试失败了,但是我通过这个改变设法从模型测试中得到了10秒。

如果您遇到类似我的问题,请按照以下步骤进行操作,您应该注意到速度提高2-3倍并且没有太大的缺点。

答案 2 :(得分:3)

我一直在使用以下hack来减少在垃圾收集器中花费的时间:

http://makandra.com/notes/950-speed-up-rspec-by-deferring-garbage-collection

文章提到了15%的改进,但在我的测试中,我看到使用Ruby 1.9.2,Rails 3.0.x和RSpec 2.0的比例约为25%。

此外,如果您不使用自动测试,这可能有所帮助,因此您只对已更改的代码运行测试。

最后,尝试使用RSpec“--profile”选项来识别10个最慢的例子,看看你是否可以优化最坏的罪犯的表现;在我的一个项目中,我发现只有3个测试将测试执行时间增加了一倍,因此我对其进行了“修复”,并将整个测试套件恢复到可接受的时间范围。

答案 3 :(得分:0)

您是否需要一直运行所有测试?您可以为不同的测试集设置rake任务,并运行与您最近更改的应用程序部分相关的任务。当然,大多数测试都会运行在没有改变的代码上。然后,每隔一段时间你就可以运行全套规格,以确保一切兼容。无论如何,这似乎是最简单的解决方案。

答案 4 :(得分:0)

我认为如果在RSpec中启用use_transactional_fixtures,则根本不需要使用database_cleaner。

另外,考虑使用NullDB来避免访问真实数据库,除非你需要(我的方法是永远不会在单元测试中命中数据库,只在集成测试中)。

答案 5 :(得分:0)