Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/spec/support/matchers/query_the_database.rb
Views: 11778
module Shoulda # :nodoc:1module Matchers2module ActiveRecord # :nodoc:34# Ensures that the number of database queries is known. Rails 3.1 or greater is required.5#6# Options:7# * <tt>when_calling</tt> - Required, the name of the method to examine.8# * <tt>with</tt> - Used in conjunction with <tt>when_calling</tt> to pass parameters to the method to examine.9# * <tt>or_less</tt> - Pass if the database is queried no more than the number of times specified, as opposed to exactly that number of times.10#11# Examples:12# it { is_expected.to query_the_database(4.times).when_calling(:complicated_counting_method)13# it { is_expected.to query_the_database(4.times).or_less.when_calling(:generate_big_report)14# it { is_expected.not_to query_the_database.when_calling(:cached_count)15#16def query_the_database(times = nil)17QueryTheDatabaseMatcher.new(times)18end1920class QueryTheDatabaseMatcher # :nodoc:21def initialize(times)22@queries = []23@options = {}2425if times.respond_to?(:count)26@options[:expected_query_count] = times.count27else28@options[:expected_query_count] = times29end30end3132def when_calling(method_name)33@options[:method_name] = method_name34self35end3637def with(*method_arguments)38@options[:method_arguments] = method_arguments39self40end4142def or_less43@options[:expected_count_is_maximum] = true44self45end4647def matches?(subject)48subscriber = ActiveSupport::Notifications.subscribe('sql.active_record') do |name, started, finished, id, payload|49@queries << payload unless filter_query(payload)50end5152if @options[:method_arguments]53subject.send(@options[:method_name], *@options[:method_arguments])54else55subject.send(@options[:method_name])56end5758ActiveSupport::Notifications.unsubscribe(subscriber)5960if @options[:expected_count_is_maximum]61@queries.length <= @options[:expected_query_count]62elsif @options[:expected_query_count].present?63@queries.length == @options[:expected_query_count]64else65@queries.length > 066end67end6869def failure_message_for_should70if @options.key?(:expected_query_count)71"Expected ##{@options[:method_name]} to cause #{@options[:expected_query_count]} database queries but it actually caused #{@queries.length} queries:" + friendly_queries72else73"Expected ##{@options[:method_name]} to query the database but it actually caused #{@queries.length} queries:" + friendly_queries74end75end7677def failure_message_for_should_not78if @options[:expected_query_count]79"Expected ##{@options[:method_name]} to not cause #{@options[:expected_query_count]} database queries but it actually caused #{@queries.length} queries:" + friendly_queries80else81"Expected ##{@options[:method_name]} to not query the database but it actually caused #{@queries.length} queries:" + friendly_queries82end83end8485private8687def friendly_queries88@queries.map do |query|89"\n (#{query[:name]}) #{query[:sql]}"90end.join91end9293def filter_query(query)94query[:name] == 'SCHEMA' || looks_like_schema?(query[:sql])95end9697def schema_terms98['FROM sqlite_master', 'PRAGMA', 'SHOW TABLES', 'SHOW KEYS FROM', 'SHOW FIELDS FROM', 'begin transaction', 'commit transaction']99end100101def looks_like_schema?(sql)102schema_terms.any? { |term| sql.include?(term) }103end104end105end106end107end108109110