Reputation: 45
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
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
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
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:
Upvotes: 3