如何在Rails中测试数据库调用的数量

时间:2012-06-27 09:13:44

标签: ruby-on-rails database performance unit-testing rspec

我正在rails中创建REST API。我正在使用RSpec。我想最小化数据库调用的数量,所以我想添加一个自动测试,验证作为特定操作的一部分执行的数据库调用的数量。 有没有一种简单的方法将其添加到我的测试中? 我正在寻找的是监视/记录由于单个API调用而对数据库进行的调用的一些方法。 如果使用RSpec无法做到这一点,但可以使用其他一些测试工具来完成,那也很棒。

4 个答案:

答案 0 :(得分:3)

Rails 3中最容易的事情可能是挂钩到通知api。

此订阅者

class SqlCounter< ActiveSupport::LogSubscriber

  def self.count= value
    Thread.current['query_count'] = value
  end

  def self.count
    Thread.current['query_count'] || 0
  end

  def self.reset_count
    result, self.count = self.count, 0
    result
  end

  def sql(event)
    self.class.count += 1
    puts "logged #{event.payload[:sql]}"
  end
end

SqlCounter.attach_to :active_record

将每个执行的sql语句打印到控制台并计算它们。然后你可以编写诸如

之类的规范
expect do
  # do stuff
end.to change(SqlCounter, :count).by(2)

您可能希望过滤掉一些语句,例如启动/提交事务或发出活动记录以确定表的结构。

答案 1 :(得分:1)

您可能有兴趣使用 explain 。但这不会是自动的。您需要手动分析每个操作。但也许这是一件好事,因为重要的不是db调用的数量,而是它们的性质。例如:他们使用索引吗?

检查一下:

http://weblog.rubyonrails.org/2011/12/6/what-s-new-in-edge-rails-explain/

答案 2 :(得分:0)

弗雷德里克的回答对我很有帮助,但就我而言,我也想知道每个ActiveRecord类的调用次数。我做了一些修改,最后得到了这个,以防它对别人有用。

class SqlCounter< ActiveSupport::LogSubscriber

  # Returns the number of database "Loads" for a given ActiveRecord class.
  def self.count(clazz)
    name = clazz.name + ' Load'
    Thread.current['log'] ||= {}
    Thread.current['log'][name] || 0
  end

  # Returns a list of ActiveRecord classes that were counted.
  def self.counted_classes
    log = Thread.current['log']
    loads = log.keys.select {|key| key =~ /Load$/ }
    loads.map { |key| Object.const_get(key.split.first) }
  end

  def self.reset_count
    Thread.current['log'] = {}
  end

  def sql(event)
    name = event.payload[:name]
    Thread.current['log'] ||= {}
    Thread.current['log'][name] ||= 0
    Thread.current['log'][name] += 1
  end
end

SqlCounter.attach_to :active_record

expect do
  # do stuff
end.to change(SqlCounter, :count).by(2)

答案 3 :(得分:0)

使用db-query-matchers宝石。

expect { subject.make_one_query }.to make_database_queries(count: 1)