Is there a cleaner way than then monkeypatch I’ve done below to automatically look up all tags
applied to a Spec::Item
, including not just the tags applied to the it
but also the tags applied to its parent describe
blocks?
Use case: some (but not all) of my specs hit a test database. As I do in Ruby / rspec, I want to clear the database before and after each spec example runs, so that each example starts with a clean, predictable state. I have a truncate_db
tag applied to the relevant specs:
describe MyClassThatRequiresDatabase, tags: ["truncate_db"] do
it "..." do
# this test expects a clean database state, and may mutate database,
# so we should clean the db before and after!
end
end
In my use case, I’ve written a spec/truncate_db_helper.cr
(required by my spec/spec_helper.cr
) which basically looks like this:
Spec.around_each do |example_procsy|
if !example_procsy.example.recursive_tags.includes?("truncate_db")
example_procsy.run
next
end
MyDbPool.truncate_entire_database_for_testing!
example_procsy.run
MyDbPool.truncate_entire_database_for_testing!
end
I was surprised when my first attempt at example_procsy.example.tags.includes?("truncate_db")
only covered the case where I applied my tag directly to the it
level, but it did not cover when I’d applied the tag to an entire describe
group.
In order to get the desired behavior, I had to monkeypatch a spec/recursive_tags_helper.cr
to add a #recursive_tags
method as follows:
require "spec"
module Spec
module Item
protected def recursive_tags_helper(acc : Set(String)) : Nil
acc.concat(self.tags.not_nil!) unless self.tags.nil?
return if self.parent.is_a?(Spec::RootContext)
self.parent.as(Spec::ExampleGroup).recursive_tags_helper(acc)
end
def recursive_tags : Set(String)
# collect all tags, recursively looking up the tree of parent "describe" contexts
results = Set(String).new
self.as(Spec::Example | Spec::ExampleGroup).recursive_tags_helper(results)
results
end
end
end
This allows me to apply the truncate_db
tag at either the individual it
level, or at a higher describe
level.
Rspec seems to do something similar with its metadata collection, and this functionality is used commonly on the Rails side (type: :model
vs type: :request
for example).
Does this belong in stdlib? (Happy to make it into a PR.) Any suggestions for a better name than #recursive_tags
?