Zoopy
Zoopy

Reputation: 77

Rails - RSpec NoMethodError: undefined method

I'm trying to test a very simple method that takes in 2 numbers and uses them to work out a percentage. However, when I try and run the tests it fails with the following error:

NoMethodError: undefined method `pct' for Scorable:Module
./spec/models/concerns/scorable_spec.rb:328:in `block (2 levels) in 
<top (required)>'
./spec/rails_helper.rb:97:in `block (3 levels) in <top (required)>'
./spec/rails_helper.rb:96:in `block (2 levels) in <top (required)>'
-e:1:in `<main>'

Here's my spec file for the module:

require 'rails_helper'
RSpec.describe Scorable, :type => :concern do

  it "pct should return 0 if den is 0 or nil" do
    expect(Scorable.pct(nil, 15)).to eq(0)
    expect(Scorable.pct(0, 15)).to eq(0)
  end

end

Here is the pct method located in Scorable.rb:

def pct(num,den)
  return 0 if num == 0 or num.nil?
  return (100.0 * num / den).round
end

And here's my rspec_helper:

 if ENV['ENABLE_COVERAGE']
   require 'simplecov'
   SimpleCov.start do
   add_filter "/spec/"
   add_filter "/config/"
   add_filter '/vendor/'

   add_group 'Controllers', 'app/controllers'
   add_group 'Models', 'app/models'
   add_group 'Helpers', 'app/helpers'
   add_group 'Mailers', 'app/mailers'
   add_group 'Views', 'app/views'
 end
end

RSpec.configure do |config|
  config.expect_with :rspec do |expectations|
  expectations.include_chain_clauses_in_custom_matcher_descriptions = 
  true
end
config.raise_errors_for_deprecations!

 config.mock_with :rspec do |mocks|
   mocks.verify_partial_doubles = true
 end
end

I'm very new to RSpec and have been puzzling over this one for more than a day. It's definitely pointing to an existing method, as when I use Go To Declaration in RubyMine it opens the method declaration. Can anyone maybe shed some light for me on this one? I'm sure I'm overlooking something incredibly simple.

Upvotes: 0

Views: 8247

Answers (1)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121010

To make the module method callable with Module.method notation is should be declared in module scope.

module Scorable
  def self.pct(num,den)
    return 0 if num == 0 or num.nil?
    return (100.0 * num / den).round
  end
end

or:

module Scorable
  class << self
    def pct(num,den)
      return 0 if num == 0 or num.nil?
      return (100.0 * num / den).round
    end
  end
end

or with Module#module_function:

module Scorable
  module_function
  def pct(num,den)
    return 0 if num == 0 or num.nil?
    return (100.0 * num / den).round
  end
end

Note, that the latter declares both module method and normal instance method within this module.


Sidenote: using return in the very last line of the method is considered a code smell and should be avoided:

module Scorable
  def self.pct(num,den)
    return 0 if num == 0 or num.nil?
    (100.0 * num / den).round
  end
end

Upvotes: 2

Related Questions