Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Commit 79c1782

Browse files
committed
Add config.when_first_matching_example_defined.
1 parent ceecf84 commit 79c1782

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed

Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ Enhancements:
55

66
* Remove unneeded `:execution_result` example groups metadata, saving a
77
bit of memory. (Myron Marston, #2172)
8+
* Add new `config.when_first_matching_example_defined` hook. (Myron
9+
Marston, #2175)
810

911
Bug Fixes:
1012

lib/rspec/core/configuration.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1597,6 +1597,39 @@ def define_derived_metadata(*filters, &block)
15971597
@derived_metadata_blocks.append(block, meta)
15981598
end
15991599

1600+
# Defines a callback that runs after the first example with matching
1601+
# metadata is defined. If no examples are defined with matching metadata,
1602+
# it will not get called at all.
1603+
#
1604+
# This can be used as an alternative to `before(:suite)` to define some
1605+
# expensive one-time setup that will be skipped if none of the loaded
1606+
# examples need it.
1607+
#
1608+
# @example
1609+
# RSpec.configure do |config|
1610+
# # only spend time bootstrapping the database if any
1611+
# # examples tagged with `:db` have been loaded.
1612+
# config.when_first_matching_example_defined(:db) do
1613+
# ORM.bootstrap_db
1614+
# end
1615+
# end
1616+
def when_first_matching_example_defined(*filters, &block)
1617+
specified_meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering)
1618+
1619+
callback = lambda do |example_or_group_meta|
1620+
# Example groups do not have `:example_group` metadata
1621+
# (instead they have `:parent_example_group` metadata).
1622+
return unless example_or_group_meta.key?(:example_group)
1623+
1624+
# Ensure the callback only fires once.
1625+
@derived_metadata_blocks.items_for(specified_meta).delete(callback)
1626+
1627+
block.call
1628+
end
1629+
1630+
@derived_metadata_blocks.append(callback, specified_meta)
1631+
end
1632+
16001633
# @private
16011634
def apply_derived_metadata_to(metadata)
16021635
@derived_metadata_blocks.items_for(metadata).each do |block|

spec/rspec/core/configuration_spec.rb

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,6 +1554,111 @@ def exclude?(line)
15541554
end
15551555
end
15561556

1557+
describe "#when_first_matching_example_defined" do
1558+
include_examples "warning of deprecated `:example_group` during filtering configuration", :when_first_matching_example_defined
1559+
1560+
it "runs the block when the first matching example is defined" do
1561+
sequence = []
1562+
RSpec.configuration.when_first_matching_example_defined(:foo) do
1563+
sequence << :callback
1564+
end
1565+
1566+
RSpec.describe do
1567+
example("ex 1")
1568+
sequence << :before_first_matching_example_defined
1569+
example("ex 2", :foo)
1570+
sequence << :after_first_matching_example_defined
1571+
end
1572+
1573+
expect(sequence).to eq [:before_first_matching_example_defined, :callback, :after_first_matching_example_defined]
1574+
end
1575+
1576+
it "does not fire when later matching examples are defined" do
1577+
sequence = []
1578+
RSpec.configuration.when_first_matching_example_defined(:foo) do
1579+
sequence << :callback
1580+
end
1581+
1582+
RSpec.describe do
1583+
example("ex 1", :foo)
1584+
sequence.clear
1585+
1586+
sequence << :before_second_matching_example_defined
1587+
example("ex 2", :foo)
1588+
sequence << :after_second_matching_example_defined
1589+
end
1590+
1591+
expect(sequence).to eq [:before_second_matching_example_defined, :after_second_matching_example_defined]
1592+
end
1593+
1594+
it "does not run the block if no matching examples are defined" do
1595+
sequence = []
1596+
RSpec.configuration.when_first_matching_example_defined(:foo) do
1597+
sequence << :callback
1598+
end
1599+
1600+
RSpec.describe do
1601+
example("ex 1")
1602+
example("ex 2", :bar)
1603+
end
1604+
1605+
expect(sequence).to eq []
1606+
end
1607+
1608+
it 'does not run the block if groups match the metadata but no examples do' do
1609+
called = false
1610+
RSpec.configuration.when_first_matching_example_defined(:foo => true) do
1611+
called = true
1612+
end
1613+
1614+
RSpec.describe "group 1", :foo => true do
1615+
end
1616+
1617+
RSpec.describe "group 2", :foo => true do
1618+
example("ex", :foo => false)
1619+
end
1620+
1621+
expect(called).to be false
1622+
end
1623+
1624+
it "still runs after the first matching example even if there is a group that matches earlier" do
1625+
sequence = []
1626+
RSpec.configuration.when_first_matching_example_defined(:foo) do
1627+
sequence << :callback
1628+
end
1629+
1630+
RSpec.describe "group", :foo do
1631+
end
1632+
1633+
RSpec.describe do
1634+
example("ex 1")
1635+
sequence << :before_first_matching_example_defined
1636+
example("ex 2", :foo)
1637+
sequence << :after_first_matching_example_defined
1638+
end
1639+
1640+
expect(sequence).to eq [:before_first_matching_example_defined, :callback, :after_first_matching_example_defined]
1641+
end
1642+
1643+
context "when a group is defined with matching metadata" do
1644+
it "runs the callback after the first example in the group is defined" do
1645+
sequence = []
1646+
RSpec.configuration.when_first_matching_example_defined(:foo) do
1647+
sequence << :callback
1648+
end
1649+
1650+
sequence << :before_group
1651+
RSpec.describe "group", :foo do
1652+
sequence << :before_example
1653+
example("ex")
1654+
sequence << :after_example
1655+
end
1656+
1657+
expect(sequence).to eq [:before_group, :before_example, :callback, :after_example]
1658+
end
1659+
end
1660+
end
1661+
15571662
describe "#add_setting" do
15581663
describe "with no modifiers" do
15591664
context "with no additional options" do

0 commit comments

Comments
 (0)