class RuboCop::Cop::RSpec::AggregateExamples
end
expect(number).to be_odd
expect(number).to be_positive
specify do
@example AddAggregateFailuresMetadata: false
end
expect(number).to be_odd
expect(number).to be_positive
specify(:aggregate_failures) do
# Metadata set using a symbol
@example AddAggregateFailuresMetadata: true (default)
the example instead of aborting on the first one.
a single example. The latter means to run all the expectations in
confused. The former stands for putting several expectations to
The terms “aggregate examples” and “aggregate failures” not to be
ones, commonly known as fail-fast.
when it’s not desired to make expectations after previously failed
example or not. The option not to add metadata can be also used
can be configured to add ‘:aggregate_failures` metadata to the
To match the style being used in the spec suite, AggregateExamples<br><br>end<br>end<br>metadata = true
unless metadata.key?(:aggregate_failures)
config.define_derived_metadata do |metadata|
# spec/spec_helper.rb
@example Globally enable `aggregate_failures`
or [enabled globally](relishapp.com/rspec/rspec-core/docs/expectation-framework-integration/aggregating-failures#enable-failure-aggregation-globally-using-%60define-derived-metadata%60).
It can be [used in metadata form](relishapp.com/rspec/rspec-core/docs/expectation-framework-integration/aggregating-failures#use-%60:aggregate-failures%60-metadata),
handy with aggregated examples.
not to fail the example on first unmet expectation that might come
RSpec [comes with an `aggregate_failures` helper](relishapp.com/rspec/rspec-expectations/docs/aggregating-failures)
specific order.
3. Aggregation of block syntax with non-block syntax should be in a
doesn’t provide a negated variant.
of the ‘not_to` expectations is barely possible when a matcher
2. Aggregation should use composition with an `.and`. Also, aggregation
detect that at all.
If the subject is defined in a `shared_context`, it’s impossible to
it { is_expected.to do_something }
be, depending on how the subject is defined:
following looks like an example with non-block syntax, but it might
1. ‘subject { -> { … } }` syntax being hard to detect, e.g. the
Block expectation syntax is deliberately not supported due to:
end
end
expect(multiply_by(3)).to be_multiple_of(3)
specify do
end
expect(multiply_by(2)).to be_multiple_of(2)
specify do
describe do
# fair - subject has side effects
end
end
is_expected.to be_prime
expect(number).to be_odd
expect(number).to be_positive
specify do
describe do
# good
end
it { is_expected.to be_prime }
end
expect(number).to be_odd
expect(number).to be_positive
specify do
describe do
# bad
@example
context initialization.
This cop is primarily for reducing the cost of repeated expensive
@see github.com/rubocop-hq/rspec-style-guide#expectation-per-example<br><br>Checks if example groups contain two or more aggregatable examples.
def aggregated_example(examples, metadata)
def aggregated_example(examples, metadata) base_indent = " " * examples.first.source_range.column metadata = metadata_for_aggregated_example(metadata) [ "#{base_indent}specify#{metadata} do", *examples.map { |example| transform_body(example, base_indent) }, "#{base_indent}end\n" ].join("\n") end
def drop_example(corrector, example)
def drop_example(corrector, example) aggregated_range = range_by_whole_lines(example.source_range, include_final_newline: true) corrector.remove(aggregated_range) end
def example_clusters(all_examples)
Clusters of examples in the same example group, on the same nesting
def example_clusters(all_examples) all_examples .select { |example| example_with_expectations_only?(example) } .group_by { |example| metadata_without_aggregate_failures(example) } .select { |_, examples| examples.count > 1 } end
def example_clusters_for_autocorrect(example_node)
Clusters of examples that can be aggregated without losing any
def example_clusters_for_autocorrect(example_node) examples_in_group = example_node.parent.each_child_node(:block) .select { |example| example_for_autocorrect?(example) } example_clusters(examples_in_group) end
def message_for(_example, first_example)
def message_for(_example, first_example) format(MSG, first_example.loc.line) end
def new_body(node)
def new_body(node) node.body.source end
def on_block(node)
def on_block(node) example_group_with_several_examples(node) do |all_examples| example_clusters(all_examples).each do |_, examples| examples.drop(1).each do |example| add_offense(example, message: message_for(example, examples[0])) do |corrector| clusters = example_clusters_for_autocorrect(example) clusters.each do |metadata, examples| range = range_for_replace(examples) replacement = aggregated_example(examples, metadata) corrector.replace(range, replacement) examples.drop(1).map { |example| drop_example(corrector, example) } end end end end end end
def transform_body(node, base_indent)
def transform_body(node, base_indent) "#{base_indent} #{new_body(node)}" end