【问题标题】:Rails 3.0.7 -> How do you get your tests to run faster?Rails 3.0.7 -> 如何让测试运行得更快?
【发布时间】:2011-05-22 10:27:32
【问题描述】:

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

所以在继续之前,我想尝试找到减少运行这些测试所需时间的方法 - 希望不必实际更改测试。

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

我认为 database_cleaner 正在减慢它们的速度,但没有它我不知道如何测试查询和其他东西。

使用带有 ":memory:" 选项的 sqlite3 似乎只减少了大约 10 秒(有点令人失望的结果......)

我能做些什么来真正加快我的测试速度?

【问题讨论】:

  • 这 518 个单元/功能/集成测试之间的区别是什么?
  • 除了查询测试之外,它们都是单元。我会说他们中的大多数是单位。 20-25% 是集成(查询......当你想到它时,这是一种单位)。没有进行高级别的测试。我测试的最高级别是控制器,它使用存根
  • 对于那些关注这个的人来说,问题是 factory_girl。它超级慢。 9 个人觉得这个问题很有趣,我敢打赌,你们 9 个人都像我一样使用 factory_girl。它会导致每次测试浪费 0.25 到 0.35 秒的时间!这是不可接受的,我正在寻求纠正它......或者在继续之前将其换成其他东西。
  • 好吧,我已将所有 Factory.build/Factory.create 调用更改为简单的 Ruby MyClass.new 调用。现在 250 个控制器测试在大约 5 秒内运行,而不是天知道要多长时间。它很快。而且我必须告诉你,老实说,不使用工厂并不是那么痛苦。只需设置您需要的值,如果它很烦人,请将其存根。问题解决了。我不知道为什么我们使用工厂女孩。从字面上看,它比简单地自己制作实例效率低了许多数量级。我不知道 Factory.build 内部到底发生了什么……但它臃肿且效率低下。
  • 是的,在不合适的情况下删除 Factory girl 或调用 . build() 并尽可能使用 :default_strategy => :build 。总体速度提高了 75%,在某些情况下,速度提高了 2000%。

标签: ruby-on-rails ruby performance testing


【解决方案1】:

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

  • 使用 spork - Spork 将一次性完成所有引导和环境要求,将其保存在内存中,然后仅重新加载您的测试。这对快速运行测试有很大帮助。
  • 明智地测试您的测试方式 - 就我个人而言,我的工作流程是开发测试,运行完整的测试套件以查看失败的地方,然后只运行新的/失败的测试,直到我获得这些测试。最后,当我全部完成后,我最后一次运行该套件,看看我是否回归了其他东西。
  • 清理您的 Gemfile - Rails 启动的大部分时间都花在了 require 上,如果您有不再使用的 gem,那么您会增加加载时间而没有任何好处。取出你不使用的任何东西,并考虑将只使用在一个或两个位置的东西放在一个命名组中,这样你就可以在执行期间手动要求它们(注意这一点 - 你正在用初始加载速度换取请求性能,这非常适合开发,但不适合生产)
  • 明智地选择存根和模拟的内容 - 例如,如果您真的在进行单元测试,那么您应该避免接触数据库,或者至少避免接触控制器测试。想想班级的真正责任是什么。控制器不负责保存记录,它负责告诉模型保存记录。甚至模型也不负责将内容保存在数据库中,它们负责告诉 ActiveRecord。
  • 如果你能很好地解决问题,请考虑不包括 Rails。您需要在测试中删除几乎所有 ActiveRecord 功能,但如果您能做到这一点,您会看到测试时间大量减少(可能超过一个数量级) .

【讨论】:

  • 所以我应该停止使用 Rails 吗?我正在做你建议的所有其他事情;)
  • @Ryan Brunner:我是认真的。我觉得因为有 9 个人提出了您的帖子,所以这可能是最佳实践的答案......那么如果您已经这样做了会发生什么? ://
  • 如果你认真地做我所说的 100%(特别是删除 ActiveRecord),那么你应该能够在测试中不包含 Rails 而没有任何问题。 “停止使用 Rails”,我的意思是不要将它包含在你的测试中,不要切换到不同的框架。
  • 顺便说一句,如果你只有 518 个测试,并且你对存根/模拟非常完美,那么你要么运行了一些非常密集的代码,要么你有一个庞大的 Gemfile,或者你的计算机非常需要的升级。那些 518 是否包括 Cucumber 或任何带有无头浏览器的东西?
  • @Ryan Brunner:不,没有黄瓜。一切都是基本的 RSpec。我正在测试模型、控制器、查询等。我尝试尽可能打开 render_views。我有英特尔四核机器......它不会那么慢......可以吗?我的 gemfile 也很小。它与我在 github 中看到的 rails 应用程序完全不同。甚至没有关闭。
【解决方案2】:

Ryan Brunner 提供了很多很好的建议。他所说的一切大体上都是真的,但不适用于我。

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

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

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

我的整个测试套件现在只需不到 30 秒,而不是最多 90 秒。通过进行这些更改,通常可以提高 300% 的速度……但是当涉及到控制器测试时,我得到了 2000% 的速度提高——而且我已经陷入困境了!所有这些性能开销都归因于Factory.build!这就是大部分收益的来源。

当然,我回到我的模型并尽可能使用Factory.build 或简单地使用MyClass.new

我还尽可能在factories.rb 中添加:default_strategy => :build,以防止Factory Girl 访问数据库。如果您问我,这应该是默认设置,因为此更改仅导致 1 个测试失败,但仅通过此更改,我就设法从模型测试中获得了整整 10 秒的时间。

如果您遇到像我这样的问题,请按照以下步骤操作,您应该会发现速度提高了 2-3 倍,而且没有太大的缺点。

【讨论】:

    【解决方案3】:

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

    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 个测试使我的 150 个测试的测试执行时间增加了一倍,所以我“修复”了它们,它使整个测试套件恢复到可接受的时间范围。

    【讨论】:

    • 嗯,我尝试了延迟垃圾回收,但并没有太大的不同。这是 85 秒......这可能只是误差范围:/ 不过值得一试!接下来我将尝试配置文件选项
    • 酷!我的应用程序改进了大约 35%。
    • 我嫉妒了。这对我没有任何作用:(
    【解决方案4】:

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

    【讨论】:

    • 嗯,这就是我目前正在做的事情。不过,我可以在 70 秒内运行 1800 个 java 测试......所以必须有一种方法可以更接近这一点。
    【解决方案5】:
    【解决方案6】:

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

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

    【讨论】:

    • 嗯,就是这样 - 我有 use_transactional_fixture = true,但是当我测试查询时我还是遇到了数据库故障。我必须制作一些“迷你数据库”以确保我的查询正常工作。不幸的是,use_transaction_fixtures 不起作用——可能是因为我使用 Factory Girl 来创建模型(我不太关心夹具)。但是一旦我这样做了,我需要 database_cleaner 否则测试将无法通过重复运行的测试
    • 我认为关于 use_transactional_fixtures 和 Databasecleaner 的观点可能无论如何都没有实际意义。我尝试使用干净的数据库和禁用的数据库清洁器进行测试......并且测试需要相同的时间......所以我怀疑这会改善我的情况:(
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-15
    • 2018-02-11
    • 2010-11-06
    • 2023-03-26
    • 1970-01-01
    • 2011-05-10
    相关资源
    最近更新 更多