RSpec: How to define classes for specs
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
).
Copydescribe 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 Archive about this topic.
1. Defining the constant on the example class
Copydescribe 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
Copydescribe 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
Copydescribe 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)
.
Your development team has a full backlog of feature requests, chores and refactoring coupled with deadlines? We are familiar with that. With our "DevOps as a Service" offering, we support developer teams with infrastructure and operations expertise.