0

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.

3
  • 1
    Please show the source code of Scorable module, specifically the pct method. Commented Jul 5, 2017 at 12:56
  • 1
    Rubymine is not perfect ... Commented Jul 5, 2017 at 12:57
  • @mudasobwa Have updated the question with that method definition. Commented Jul 5, 2017 at 12:58

1 Answer 1

2

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
Sign up to request clarification or add additional context in comments.

1 Comment

Ah, thank you! I can't believe it was that simple. I'll definitely remember the module_function thing. Module and instance methods still regularly trip me up in Rails. Regarding the return statement, it's a very bad habit I'm struggling to shed. I come from a C# background and started Rails 1 1/2 months ago and my instincts still regularly rebel against many Rails conventions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.