Skip to content

Commit

Permalink
Add minitest assert_perform_linear_number_of_queries
Browse files Browse the repository at this point in the history
  • Loading branch information
caalberts authored and Earendil95 committed Nov 26, 2020
1 parent aa1e54d commit 57685df
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 2 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## master (unreleased)

- Fix table stats summary when queries use backticks to surround table names ([@andrewhampton])
- Add rspec matcher for linear query. ([@caalberts][])
- Add support to test for linear query. ([@caalberts][])

## 0.5.0 (2020-09-07)

Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,18 @@ def test_no_n_plus_one_error
end
```

You can also use `assert_perform_linear_number_of_queries` to test for linear queries:

```ruby
def test_no_n_plus_one_error
populate = ->(n) { create_list(:post, n) }

assert_perform_linear_number_of_queries(slope: 1, populate: populate) do
Post.find_each { |p| p.user.name }
end
end
```

You can also specify custom scale factors or filter patterns:

```ruby
Expand Down
33 changes: 33 additions & 0 deletions lib/n_plus_one_control/minitest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,29 @@ def assert_perform_constant_number_of_queries(
assert counts.max == counts.min, NPlusOneControl.failure_message(:constant_queries, queries)
end

def assert_perform_linear_number_of_queries(
slope: 1,
populate: nil,
matching: nil,
scale_factors: nil,
warmup: nil
)

raise ArgumentError, "Block is required" unless block_given?

warming_up warmup

@executor = NPlusOneControl::Executor.new(
population: populate || population_method,
matching: matching || NPlusOneControl.default_matching,
scale_factors: scale_factors || NPlusOneControl.default_scale_factors
)

queries = @executor.call { yield }

assert linear?(queries, slope: slope), NPlusOneControl.failure_message(:linear_queries, queries)
end

def current_scale
@executor&.current_scale
end
Expand All @@ -42,6 +65,16 @@ def warming_up(warmup)
def population_method
methods.include?(:populate) ? method(:populate) : nil
end

def linear?(queries, slope:)
queries.each_cons(2).all? do |pair|
scales = pair.map(&:first)
query_lists = pair.map(&:last)

actual_slope = (query_lists[1].size - query_lists[0].size) / (scales[1] - scales[0])
actual_slope <= slope
end
end
end
end

Expand Down
56 changes: 55 additions & 1 deletion tests/minitest_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

require_relative "test_helper"

class TestMinitest < Minitest::Test
class TestMinitestConstantQueries < Minitest::Test
def test_no_n_plus_one_error
populate = ->(n) { create_list(:post, n) }

Expand Down Expand Up @@ -48,6 +48,60 @@ def test_no_n_plus_one_error_with_matching
end
end

class TestMinitestLinearQueries < Minitest::Test
def test_constant_queries
populate = ->(n) { create_list(:post, n) }

assert_perform_linear_number_of_queries(slope: 1, populate: populate) do
Post.preload(:user).find_each { |p| p.user.name }
end
end

def test_no_n_plus_one_error
populate = ->(n) { create_list(:post, n) }

assert_perform_linear_number_of_queries(slope: 1, populate: populate) do
Post.find_each { |p| p.user.name }
end
end

def test_with_n_plus_one_error
populate = ->(n) { create_list(:post, n) }

e = assert_raises Minitest::Assertion do
assert_perform_linear_number_of_queries(slope: 1, populate: populate) do
Post.find_each { |p| "#{p.user.name} #{p.category.name}" }
end
end

assert_match "Expected to make linear number of queries", e.message
assert_match "5 for N=2", e.message
assert_match "7 for N=3", e.message
end

def test_no_n_plus_one_error_with_scale_factors
populate = ->(n) { create_list(:post, n) }

assert_perform_linear_number_of_queries(
populate: populate,
scale_factors: [2, 3]
) do
Post.find_each { |p| p.user.name }
end
end

def test_no_n_plus_one_error_with_matching
populate = ->(n) { create_list(:post, n) }

assert_perform_linear_number_of_queries(
populate: populate,
matching: /users/
) do
Post.find_each { |p| p.user.name }
end
end
end

class TestMinitestPopulateMethod < Minitest::Test
def populate(n)
create_list(:post, n)
Expand Down

0 comments on commit 57685df

Please sign in to comment.