Testing modules in RSpec
I found a better solution in rspec homepage. Apparently it supports shared example groups. From https://www.relishapp.com/rspec/rspec-core/v/2-13/docs/example-groups/shared-examples!
Shared Example Groups
You can create shared example groups
and include those groups into other
groups.Suppose you have some behavior that
applies to all editions of your
product, both large and small.First, factor out the “shared”
behavior:
shared_examples_for "all editions" do
it "should behave like all editions" do
end
end
then when you need define the behavior
for the Large and Small editions,
reference the shared behavior using
the it_should_behave_like() method.
describe "SmallEdition" do
it_should_behave_like "all editions"
it "should also behave like a small edition" do
end
end
How to test module in a module with rspec?
First of all, module A do
syntax seems to be wrong - there shouldn't be a do
word. The second thing is that you tries to call foo method on a module A::B
(there is no class method defined on A::B
- foo is an instance method). Your code should looks like:
module A
module B
def self.foo
end
end
end
*(In this case, you can call A::B.foo
)
OR if you want to have foo as an instance method:
module A
module B
def foo
end
end
end
In this case you cannot call A::B.foo
, but you can create a class, which can include A::B
module:
class Test
include A::B
end
Now you can call Test.new.foo
Regarding rspec testing:
- class method: as you described
- instance method:
- define Test class in your spec and then test this class
- include this module to your rspec context (ugly way)
Testing Ruby Modules with rspec
You can create an anonymous class in your tests:
describe A do
let(:extended_class) { Class.new { extend A } }
let(:including_class) { Class.new { include A } }
it "works" do
# do stuff with extended_class.say_hello
# do stuff with including_class.new.say_hello
end
end
To see something similar in real code, I've used this strategy for testing my attr_extras lib.
That said, include
and extend
are standard features of Ruby, so I wouldn't test that every module works both when including and when extending – that's usually a given.
If you create a named class in the test, like you do in your question, I believe that class will exist globally for the duration of your test run. So this class will leak between every test of your test suite, potentially causing conflicts somewhere.
If you use let
to create an anonymous class, it will only be available inside this particular test. There is no global constant pointing to it that could conflict with other tests.
You could also use RSpec's stub_const
to get a constant that doesn't leak, if you need to:
stub_const("MyClass", Class.new { … })
# do stuff with MyClass
Note that you'd run stub_const
inside a before
or it
. Not just at the top of the file or in the class context.
How do I use modules in `spec/support/` for Ruby on Rails RSpec tests
Nothing in spec
is autoloaded by default.
Typically one adds a line to load spec/support files in spec/rails_helper.rb
Dir[Rails.root.join('spec/support/**/*.rb')].sort.each { |f| require f }
Note that this will add load time to all test runs regardless of whether they use your support files. This is fine for general use support files, but not such a good idea for support files specific to a single class. Those test classes will pollute every test.
Instead, consider writing them as a shared context and including as needed. Put them in a shared_context directory. You can also load all shared contexts and examples.
Dir[Rails.root.join('spec/**/shared*/*.rb')].sort.each { |f| require f }
Note that spec/support/page_objects/foo/foo_bar.rb would normally be for Foo::FooBar. FooBar should go into spec/support/page_objects/foo_bar.rb. This will have no material effect on your code, but it's good to have naming consistent with Rails standards.
How to test ruby module methods with block using Rspec?
The easiest way is to insert a double as the yield
argument, which you can make an assertion on.
payload = Payload.new
allow(Payload).to receive(:new).and_return(payload)
test_target
expect(payload.my_text).to eq 'payload text'
Alternatively you could also use expect_any_instance_of
, but I'd always prefer to use a specific double instead.
How to test private helper(module) method in rspec
create a dummy class and access private method using .send(:private_method, args)
example
obj = Class.new { extend AppHelper }
obj.send(:sum, 1,2)
How to include several modules in RSpec?
This should be done in spec_helper.rb
. You can conditionally include
modules depending on the spec type.
RSpec.configure do |config|
config.include MailersHelper, type: :mailer
end
Any spec in spec/mailers
will now automatically include MailersHelper
.
You can trigger this manually by doing
RSpec.describe FooConstant, type: :mailer do
# ... etc
end
in cases where the spec doesn't reside in spec/mailers
but you want to treat it like one.
Ruby: How to include outside module in Rspec test
I should have seen the problem immediately, but didn't. You see the difference between :
lib/my_class.rb:9:in `<class:MyClass>': undefined method `open_file' for MyClass:Class (NoMethodError)
when open_file
appears in the body of MyClass, and :
lib/my_class.rb:16:in `m': undefined method `open_file' for #<MyClass:0x007fbb0d10cc30> (NoMethodError)
if I put it in a def
:
def m
@error_file = open_file('error_file.txt')
In the first case, you are in the body of MyClass
, where open_file
is not defined. In the second case, I had removed the include Common
.
To do my research, I've defined the minimum necessary to reproduce the error.
File .../lib/file_utils.rb
, same as yours.
File .../lib/common.rb
:
require_relative 'file_utils'
module Common
puts "Common instance methods before include : #{instance_methods(true).sort}"
include FileUtils
puts "Common instance methods after include : #{instance_methods(true).sort}"
puts "Common class methods before extend : #{singleton_methods(true).sort}"
extend FileUtils
puts "Common class methods after extend : #{singleton_methods(true).sort}"
end
File .../lib/my_class.rb
:
require_relative 'common'
class MyClass
# puts "MyClass methods before include : #{instance_methods(true).sort}"
include Common
# puts "MyClass methods after include : #{instance_methods(true).sort}"
puts "self=#{self}"
puts "MyClass class methods before extend : #{singleton_methods(true).sort}"
extend Common
puts "MyClass class methods after extend : #{singleton_methods(true).sort}"
@error_file = open_file('error_file.txt')
puts "in MyClass error_file=#{@error_file}"
def m
@error_file = open_file('error_file.txt')
puts "in m error_file=#{@error_file}"
end
end
MyClass.new.m
Execution :
$ ruby -w lib/my_class.rb
Common instance methods before include : []
Common instance methods after include : [:open_file]
Common class methods before extend : []
Common class methods after extend : [:open_file]
self=MyClass
MyClass class methods before extend : []
MyClass class methods after extend : [:open_file]
in MyClass error_file=#<File:0x007ff2621fcdc0>
in m error_file=#<File:0x007ff2621fc938>
Explanation
Due to the way you use @error_file = open_file('error_file.txt')
, you are in the body of MyClass
, which is executed only when the interpreter reads the class definition. When a method like open_file
is used without a receiver, it is sent to the implicit receiver self
, which is MyClass
. But as it is defined, open_file
is not a class method, it is an instance method.
If you need a class method (more exactly a singleton method), you have to define it as
def self.open_file(file_name)
or use extend <module>
.
Testing a module with RSpec 3
It might be getting an instance and class level method confused.
Try this to be sure it's really testing on the Class-level method
class MyClass
include MyModule
end
it "should have an active relation on method" do
expect(MyClass.method).to be_a(ActiveRecord::Relation)
end
Related Topics
Block Comments in HTML.Erb Templates in Rails
Best Way to Add Comments in Erb
Private Module Methods in Ruby
How to "Activate" a Different Version of a Particular Gem
Why Can't I Install Rails on Lion Using Rvm
How to Access Method Arguments in Ruby
Installing Vim with Ruby Support (+Ruby)
What Is Java Interface Equivalent in Ruby
What Is the Purpose of "!" and "" at the End of Method Names
Why Are All Rails Helpers Available to All Views, All the Time? How to Disable This
Using Send_File to Download a File from Amazon S3
How to Create a Class Instance from a String Name in Ruby
Equivalent of .Try() for a Hash to Avoid "Undefined Method" Errors on Nil
Is There a Better Way of Checking Nil or Length == 0 of a String in Ruby
Get Index of Array Element Faster Than O(N)
Performance of Arrays and Hashes in Ruby
How to Use Activerecord in a Ruby Script Outside Rails
Using Rails Migration on Different Database Than Standard "Production" or "Development"