Testing Modules in Rspec

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



Leave a reply



Submit