It can be difficult to test the different scenarious for a module intended for use by including in another class.
If the class is statically defined in the spec, any later definitions extend, not replace, the first definition - which can cause test issues and breaks isolation between examples.
RSpec.describe SomeModule do
class SomeIncluder
include SomeModule
end
it "configures something" do
SomeIncluder.some_config = :some_val
end
it "does something else" do
puts SomeIncluder.some_config
# => :some_val <= No example isolation
end
context "when some other scenario" do
class SomeIncluder
include SomeModule
some_other_config :some_other_val
end
it "does something else"
puts SomeIncluder.some_config
# => :some_val <= defining class re-opens the class defintion (doesn't replace)
end
end
...
Alternatively, N differently named class definitions for N different scenarios is hard to manage.
A better solution is to create the class anonymously for the given scenario:
RSpec.describe SomeModule do
let(:object) { klass.new }
let(:klass) do
# anonymous class, unique every time it is created
Class.new do
include SomeModule
end
end
context "when configured some other way" do
let(:klass) do
Class.new do
include SomeModule
some_configuration :some_value
end
end
end
...
RSpec.describe SomeModule do
let(:object) { klass.new }
let(:klass) do
# anonymous class, unique every time it is created
k = Class.new do
include SomeModule
end
Object.const_set "SomeModuleIncluder#{rand(10000)}", k
end
...