RSpec allows defining methods inside describe
/context
blocks which will only exist inside them.
However, classes (or any constants, for that matter) will not be affected by this. If you define them in your specs, they will exist globally. This is because of how RSpec works (short story: instance_eval
).
describe Notifier do
class TestRecord
# ...
end
let(:record) { TestRecord.new }
it { ... }
end
# TestRecord will exist here, outside of the spec!
This will come bite you at least when you try to define a class with the same name in another spec. Generally speaking, you don't want to pollute the global namespace in the first place.
Below you will find three examples how you can avoid polluting the global namespace. You can also read the Rubocop docs on LeakyConstantDeclaration Show archive.org snapshot about this topic.
1. Defining the constant on the example class
describe Notifier do
class self::TestRecord < ApplicationRecord
# ...
end
it do
expect(self.class::TestRecord.new).to be_a(ApplicationRecord)
end
end
Inside any let
or it
block, self
will be the example's instance, so self.class
points to the example class where TestRecord
was defined. Basic Ruby.
2. Defining the class and assigning to a constant
describe Notifier do
before do
test_record = Class.new(ApplicationRecord) do
# ...
end
stub_const('TestRecord', test_record)
end
it do
expect(TestRecord.new).to be_a(ApplicationRecord)
end
end
3. Defining the class and assigning to a variable
describe Notifier do
let(:test_record_class) do
Class.new(ApplicationRecord) do
# ...
end
end
it do
expect(test_record_class.new).to be_a(ApplicationRecord)
end
end
Each approach also works for Ruby modules, e.g. module self::TestModule < ParentModule; end
or test_module = Module.new(ParentModule)
.