person1
person1

Reputation: 45

How to test module in a module with rspec?

I have this set up

module A do
  module B do
     def foo
  end
end

Inside rspec I'm trying to test foo.

My rspec file is like this

describe A::B do
  describe "#foo" do
    A::B.foo ...
  end 

end

I get the error NoMethodError: undefined method foo for A::B:Module

Any ideas?

Upvotes: 1

Views: 3792

Answers (3)

Andrew Smith
Andrew Smith

Reputation: 162

A fairly tidy way of setting this up would be:

RSpec.describe A::B do                                
  subject(:object) { Object.new.extend(described_class) }

  describe '#foo' do
    expect(object.foo).to eq('whatever')
  end
end

Upvotes: 0

Aaron K
Aaron K

Reputation: 6961

::A::B#foo is not a method on the module object itself. It is a method the module makes available via the Ruby lookup hierarchy to objects which inherit it. Inheritance in this case could be extending or including the module.

class Fooish
  include A::B
end

a_foo = Fooish.new
a_foo.foo

an_object = Object.new
an_object.extend(A::B)
an_object.foo

People are different in their approaches. If I had an object that already included A::B in my code base I may use that for my tests. If I was writing a library which made A::B available for use, then I would probably test that when my module is included/extended it does what I expect.

An example of the latter, which is also a more isolated type of unit test, using RSpec may look something like the following:

RSpec.describe A::B do
  it "makes #foo available" do
    an_object = Object.new
    expect(an_object).not_to respond_to(:foo)
    an_object.extend(A::B)
    expect(an_object).to respond_to(:foo)
  end

  it "does whatever foo does" do
    an_object = Object.new
    an_object.extend(A::B)
    expect(an_object.foo).to eq "something"
  end
end

Note that the 2nd test will implicitly test the behavior of the first test. It is up to you if you wish to include both or not.

Upvotes: 1

codevolution
codevolution

Reputation: 146

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)

Upvotes: 3

Related Questions